#ifndef GTL_ENGINE_GAME_HEADER_GUARD
#define GTL_ENGINE_GAME_HEADER_GUARD

#include "gtlCore.h"
#include "gtlGUI.h"
#include "gtlNetwork.h"
#include <eastl/set.h>

namespace GTL
{
	namespace Engine
	{
		class Mode;

		class Game
		{
		public:

			Game();
			virtual ~Game();

			int Run();
			void RegisterService(Service* service);
			Service* GetService(const char* name);

			virtual Mode* CreateMode(int id, int arg1, int arg2) = 0;

			void SetNext(int id, int arg1 = 0, int arg2 = 0);

			template <typename T>
			void AddEvent(int event, void* sender, T param)
			{
				if (mCurrentMode)
					mCurrentMode->Events.PushEvent(event, sender, param);
			}

			virtual int Event(GameEvent) { return 0; }

		protected:

			virtual int StartGame() { return 0; }
			virtual int QuitGame() { return 0; }

      int mGameRole;
			
			Mode* mCurrentMode;
			int mCurrentModeID;

			eastl::multiset<Service*, ServiceSorter> mServices;
			typedef eastl::multiset<Service*, ServiceSorter> service_set;
			struct Next
			{
				int NextMode;
				int Arg1, Arg2;
				Next() { Reset(); }
				void Reset() { NextMode = Arg1 = Arg2 = 0; }
			} mNext;

			void UnloadCurrentMode();
			void DoLoading();

			class DefaultService : public Service
			{
				virtual void Start() {}
				virtual void Shutdown() {}
			};
			class PreAllService : public DefaultService /// before even a mode is loaded
			{
				virtual int GetPriority() const { return INT32_MIN; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Pre-All"; }
			} mPreAllService;
			class LoaderService : public DefaultService /// unloads and loads modes
			{
				virtual int GetPriority() const { return Service::Loader; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Loader"; }
			} mLoaderService;

			NetworkService mNetworkService;
			
			class PreUpdateService : public DefaultService /// does input, etc.
			{
				virtual int GetPriority() const { return Service::PreUpdate; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Pre-Update"; }
			} mPreUpdateService;
			class UpdateService : public DefaultService /// calls the Update function of the current mode
			{
				virtual int GetPriority() const { return Service::Update; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Update"; }
			} mUpdateService;
			class PostUpdateService : public DefaultService /// after update, performs cleanups, does garbage collection, etc.
			{
				virtual int GetPriority() const { return Service::PostUpdate; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Post-Update"; }
			} mPostUpdateService;
			class DrawService : public DefaultService /// calls the Draw function of the current mode, then draws other shit like the mode's GUI
			{
				virtual int GetPriority() const { return Service::Draw; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Draw"; }
			} mDrawService;
			class PostDrawService : public DefaultService /// swaps the display
			{
				virtual int GetPriority() const { return Service::PostDraw; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Post-Draw"; }
			} mPostDrawService;
			class PostAllService : public DefaultService /// after all services, changes the mode, etc.
			{
				virtual int GetPriority() const { return INT32_MAX; }
				virtual int Do(float dt);
				virtual const char* GetName() const { return "Post-All"; }
			} mPostAllService;
		};

		class Mode : public GTL::AutoMembers::Producible
		{
			GTL_DEFINE_PRODUCIBLE_OBJECT(Mode);
		public:
			virtual int Start() { return 0; }
			virtual int Stop() { return 0; }

			virtual int Load() { return 0; }
			virtual int Unload() { return 0; }

			virtual int Event(GameEvent) { return 0; }

			virtual int Update(float) { return 0; }
			virtual int Draw(float) { return 0; }

			EventBuffer Events;
			GUI::Control MainGUI;

			virtual void SetGame(Game* game) {};
		protected:
			virtual int OnCreate();
			virtual int OnDestroy();

			friend class Game;
			int LoadMode();
			int UnloadMode();
		};

		template <typename T>
		class GameMode : public Mode
		{
		public:
			virtual void SetGame(Game* game) { ParentGame = (T*)game; }
			T* ParentGame;
		};

	}
}

#endif